package com.binance.mbxgateway.web.service.impl;

import java.math.BigDecimal;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;

import com.binance.assetservice.api.IProductApi;
import com.binance.assetservice.vo.ProductItemVO;
import com.binance.assetservice.vo.request.product.GetProductByRuleIdRequest;
import com.binance.assetservice.vo.request.product.GetProductItemRequest;
import com.binance.assetservice.vo.request.product.GetSumCirculationBySymbolRequest;
import com.binance.assetservice.vo.request.product.UpdateTradingProductAfterSetFeeRequest;
import com.binance.assetservice.vo.response.product.ProductItemResponse;
import com.binance.master.error.BusinessException;
import com.binance.master.error.GeneralCode;
import com.binance.master.utils.FormatUtils;
import com.binance.mbxgateway.data.mapper.RuleModelMapper;
import com.binance.mbxgateway.data.models.RuleModel;
import com.binance.mbxgateway.vo.request.QueryAssetGasRequest;
import com.binance.mbxgateway.vo.request.QueryFeeRequest;
import com.binance.mbxgateway.vo.request.QueryUserProductFeeRequest;
import com.binance.mbxgateway.vo.request.RefreshTradingRuleRequest;
import com.binance.mbxgateway.web.service.IMatchboxService;
import com.binance.mbxgateway.web.service.ITradeRuleService;
import com.google.common.base.Joiner;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class TradeRuleServiceImpl extends BaseServiceImpl implements ITradeRuleService {

    private static final BigDecimal BASIS_POINT = new BigDecimal("10000");
    private static Integer DEFAULT_MAX_QTY = 0;

    @Autowired
    private IMatchboxService matchboxService;
    @Resource
    private IProductApi productApi;

    @Autowired
    private RuleModelMapper ruleModelMapper;

    @Override
    public void setAssetGas(QueryAssetGasRequest request) throws Exception {
        try {
            this.matchboxService.putExchangeGas(request.getAsset(), request.getRate().multiply(BASIS_POINT).intValue());
        } catch (HttpStatusCodeException e) {
            String errJson = e.getResponseBodyAsString();
            log.error("setAssetGas is error:{},{}", errJson, e);
            throw new BusinessException(errJson);
        } catch (Exception e) {
            log.error("setAssetGas exception: {}", e);
            throw e;
        }
    }

    @Override
    public void setUserProductFee(QueryUserProductFeeRequest request) throws Exception {
        GetProductItemRequest body = new GetProductItemRequest();
        body.setSymbol(request.getSymbol());
        ProductItemResponse response =
                this.getAPIRequestResponse(this.productApi.getProductItem(this.newAPIRequest(body)));
        if (response == null || response.getProductItems().isEmpty()) {
            throw new BusinessException(GeneralCode.PRODUCT_NOT_EXIST);
        }
        try {
            this.matchboxService.putAccountSymbolsCommission(request.getAccountId(),
                    request.getBuyFee().multiply(BASIS_POINT).intValue(),
                    request.getMakeFee().multiply(BASIS_POINT).intValue(),
                    request.getSellFee().multiply(BASIS_POINT).intValue(), request.getSymbol(),
                    request.getTakeFee().multiply(BASIS_POINT).intValue());
        } catch (HttpStatusCodeException e) {
            String errJson = e.getResponseBodyAsString();
            log.error("setUserProductFee is error:{},{}", errJson, e);
            throw new BusinessException(errJson);
        } catch (Exception e) {
            log.error("setUserProductFee exception: {}", e);
            throw e;
        }
    }

    @Override
    public void setFee(QueryFeeRequest request) throws Exception {
        GetProductItemRequest body = new GetProductItemRequest();
        body.setSymbol(request.getSymbol());
        ProductItemResponse response =
                this.getAPIRequestResponse(this.productApi.getProductItem(this.newAPIRequest(body)));
        if (response == null || response.getProductItems().isEmpty()) {
            throw new BusinessException("产品不存在");
        }
        ProductItemVO productItem = response.getProductItems().get(0);
        productItem.setBuyerCommission(request.getBuyFee());
        productItem.setSellerCommission(request.getSellFee());
        productItem.setTakerCommission(request.getSellFee());
        productItem.setMakerCommission(request.getMakeFee());

        try {
            this.matchboxService.putSymbolCommission(request.getBuyFee().multiply(BASIS_POINT).intValue(),
                    request.getMakeFee().multiply(BASIS_POINT).intValue(),
                    request.getSellFee().multiply(BASIS_POINT).intValue(), request.getSymbol(),
                    request.getTakeFee().multiply(BASIS_POINT).intValue());
        } catch (HttpStatusCodeException e) {
            String errJson = e.getResponseBodyAsString();
            log.error("setFee is error:{},{}", errJson, e);
            throw new BusinessException(errJson);
        } catch (Exception e) {
            log.error("setFee exception: {}", e);
            throw e;
        }
        UpdateTradingProductAfterSetFeeRequest updateTradingProductRequest =
                new UpdateTradingProductAfterSetFeeRequest();
        updateTradingProductRequest.setProductItem(productItem);
        this.getAPIRequestResponse(
                this.productApi.updateTradingProductAfterSetFee(this.newAPIRequest(updateTradingProductRequest)));
    }

    @Override
    public RuleModel getRuleBySymbol(String symbol) throws Exception {
        return ruleModelMapper.getRuleBySymbol(symbol);
    }

    @Override
    public void refreshTradingRule(RefreshTradingRuleRequest body) throws Exception {
        RuleModel rule = this.ruleModelMapper.getRuleByRuleId(body.getRuleId());
        GetProductByRuleIdRequest getProductByRuleIdRequest = new GetProductByRuleIdRequest();
        getProductByRuleIdRequest.setRuleId(body.getRuleId());
        ProductItemResponse productItemResponse = this.getAPIRequestResponse(
                this.productApi.getProductByRuleId(this.newAPIRequest(getProductByRuleIdRequest)));
        for (ProductItemVO product : productItemResponse.getProductItems()) {
            GetSumCirculationBySymbolRequest getSumCirculationBySymbolRequest = new GetSumCirculationBySymbolRequest();
            getSumCirculationBySymbolRequest.setSymbol(product.getSymbol());
            Double total = this.getAPIRequestResponse(
                    this.productApi.getSumCirculationBySymbol(this.newAPIRequest(getSumCirculationBySymbolRequest)));
            // 持倉量上限絕對值不爲空時以上限絕對值為主
            if ((rule.getPositionLimitValue() != null) && (rule.getPositionLimitValue() != 0)) {
                long holdingLimit = Math.round(rule.getPositionLimitValue().doubleValue());
                log.info(String.format("%s[总流通为: %s, 持仓上限为: %s", product.getSymbol(), total, holdingLimit));
                // List<String> exemptAccounts =
                // positionFilterExemptDao.fetchExemptAccounts(product.getSymbol());
                List<String> exemptAccounts = null;
                // 设置持仓上限
                this.matchboxService.postPositionFilter(holdingLimit, product.getSymbol(),
                        CollectionUtils.isNotEmpty(exemptAccounts) ? Joiner.on(",").join(exemptAccounts) : null);
            } else
            // 持倉量上限絕對值爲空時取上限百分比值
            if (rule.getPositionLimit() != null) {
                // TODO 需要确认逻辑
                if (total != null && total > 0 && rule.getPositionLimit() > 0) {
                    long holdingLimit = (long) (total * rule.getPositionLimit());
                    log.info(String.format("%s[总流通为: %s, 持仓上限为: %s", product.getSymbol(), total, holdingLimit));
                    // List<String> exemptAccounts =
                    // positionFilterExemptDao.fetchExemptAccounts(product.getSymbol());
                    List<String> exemptAccounts = null;
                    // 设置持仓上限
                    this.matchboxService.postPositionFilter(holdingLimit, product.getSymbol(),
                            CollectionUtils.isNotEmpty(exemptAccounts) ? Joiner.on(",").join(exemptAccounts) : null);
                } else if (rule.getPositionLimit() != null && rule.getPositionLimit() == 0) {
                    // 删除持仓上限
                    this.matchboxService.deletePositionFilter(product.getSymbol());
                }
            }

            if (rule.getMinTrade() != null || rule.getMaxTrade() != null || rule.getMaxTradeValue() != null
                    || rule.getStepSize() != null) {
                // set lotsize filter
                double maxTrade = 0.00;
                // 最大交易单位絕對值不爲空以絕對值爲主
                if ((rule.getMaxTradeValue() != null) && (rule.getMaxTradeValue() > 0.00))
                    maxTrade = rule.getMaxTradeValue();
                else
                    maxTrade = rule.getMaxTrade();
                log.info(String.format("Set lotsize filter for symbol: %s with minQty: %s, maxQty: %s",
                        product.getSymbol(), rule.getMinTrade(), rule.getMaxTrade()));
                this.matchboxService.postLotSizeFilter(
                        FormatUtils.getAssetNumericFormatter().format(Math.ceil(maxTrade)),
                        FormatUtils.getAssetNumericFormatter().format(rule.getMinTrade()),
                        FormatUtils.getPriceNumericFormatter().format(rule.getStepSize()), product.getSymbol());
            }
            if (rule.getMinNotional() != null) {
                this.matchboxService.postMinNotionalFilter(null,
                        FormatUtils.getPriceNumericFormatter().format(rule.getMinNotional()), product.getSymbol());
            }
            Double minPrice = rule.getMinPrice() == null ? DEFAULT_MAX_QTY : rule.getMinPrice();
            Double maxPrice = rule.getMinPrice() == null ? DEFAULT_MAX_QTY : rule.getMaxPrice();
            log.info(String.format("%s[最小价格: %s, 最大价格: %s", product.getSymbol(), minPrice, maxPrice));
            // set price filter for next day
            this.matchboxService.postPriceFilter(FormatUtils.getPriceNumericFormatter().format(maxPrice),
                    FormatUtils.getPriceNumericFormatter().format(minPrice), product.getSymbol(),
                    FormatUtils.getPriceNumericFormatter().format(rule.getTickSize()));
        }
    }

}
